#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <functional>
#include <memory>
#include <sys/socket.h>
#include "Connection.hpp"

class Channel : public Connection
{
    using handler_t = std::function<void(std::shared_ptr<Channel> channel)>;

    static const int size = 1024;

public:
    Channel(int sockfd, const InitAddr &client)
        : _sockfd(sockfd), _client_addr(client)
    {
        SetFd(sockfd);
        SetNonBlock(sockfd);
    }

    void RegisterHandler(handler_t handler)
    {
        _handler = handler;
    }

    void Recver()
    {
        _inbuffer.clear();
        // 非阻塞读
        char buffer[size];
        while (true)
        {
            buffer[0] = 0; // 清空字符串
            ssize_t n = recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                _inbuffer += buffer;
            }
            else if (n == 0)
            {
                Exceptor();
                return;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    Exceptor();
                    return;
                }
            }
        }
        if(!_inbuffer.empty())
        {
            //_handler(std::shared_ptr<Channel>(this));
            std::cout << "[ " << _sockfd << " ]: ";
            std::cout << _inbuffer;
        }
        if(!_outbuffer.empty())
        {
            Sender();
        }
    }

    void Sender()
    {
        while(true)
        {
            ssize_t n = send(_sockfd, _outbuffer.c_str(), _outbuffer.size(), 0);
            if(n > 0)
            {
                _outbuffer.erase(0, n);
                if(_outbuffer.empty())
                {
                    break;
                }
            }
            else if(n == 0)
            {
                break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    Exceptor();
                    return;
                }
            }
        }

        if(!_outbuffer.empty())
        {
            //写条件不就绪——开启对写事件的关心，之后在下一次只要就绪了reactor就会自动帮我们发完
            GetOwner()->EnableReadWrite(_sockfd, true, true);
        }
        else
        {
            //数据读完了
            GetOwner()->EnableReadWrite(_sockfd, true, false);
        }
    }

    void Excepter()
    {
        //所有的异常都被我们集成到了这个函数内部
        GetOwner()->DelConnection(_sockfd);
    }

    ~Channel()
    {
    }

private:
    // 文件描述符和读写缓冲区
    int _sockfd;
    std::string _inbuffer; // 这里采取string并不是一个特别好的选择，因为可能涉及音视频的传输
    std::string _outbuffer;

    //多路转接中发送缓冲区默认就绪的；接受缓冲区默认不就绪
    //所以读事件要常设：因为读事件一般不就绪，不关心；
    //而写事件的关心可以按需关心。那应该如何进行关心和发送呢？——直接发送
    //总是在写，导致接受缓冲区满了，写事件不就绪，此时将对写事件的关心交给epoll
    //设置对EPOLLOUT的关心，默认就要触发一次写事件的关心

    // client info
    InitAddr _client_addr;

    handler_t _handler;
};